package com.wsoft.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wsoft.constant.ConfigConstant;
import com.wsoft.constant.redis.RedisKeyConstant;
import com.wsoft.core.annotation.GetRedisCache;
import com.wsoft.core.exception.Asserts;
import com.wsoft.core.service.CommonConfigService;
import com.wsoft.core.service.CommonOrgService;
import com.wsoft.core.service.RedisService;
import com.wsoft.core.utils.JwtTokenUtil;
import com.wsoft.core.utils.LoginUserUtil;
import com.wsoft.core.utils.PasswordValidator;
import com.wsoft.core.vo.ConfigQuery;
import com.wsoft.core.vo.OrganizationVO;
import com.wsoft.entity.BaseEntity;
import com.wsoft.enums.LoginCodeEnum;
import com.wsoft.enums.PasswordLevelEnum;
import com.wsoft.service.IMemberCacheService;
import com.wsoft.service.IMemberCommonService;
import com.wsoft.service.IVerificationCodeService;
import com.wsoft.system.dto.LoginDTO;
import com.wsoft.system.entity.SysAccountEntity;
import com.wsoft.system.entity.SysMemberEntity;
import com.wsoft.system.entity.SysOrganizationEntity;
import com.wsoft.system.entity.TsConfigEntity;
import com.wsoft.system.mapper.SysAccountMapper;
import com.wsoft.system.mapper.SysMemberMapper;
import com.wsoft.system.query.account.OnlineQuery;
import com.wsoft.system.query.account.PwdQuery;
import com.wsoft.system.query.account.StyleQuery;
import com.wsoft.system.service.*;
import com.wsoft.utils.RsaUtil;
import com.wsoft.utils.Sm3EncryptionUtil;
import com.wsoft.vo.LoginUserDetail;
import com.wsoft.vo.LoginUserVO;
import com.wsoft.vo.RoleVO;
import com.wsoft.vo.config.ConfigRespVO;
import com.wsoft.vo.config.LoginInfoRespVO;
import com.wsoft.vo.config.MultipleOrgRespVO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.wsoft.constant.redis.RedisKeyConstant.USER_INFO_CACHE;

/**
 * <p>
 * 账号表 服务实现类
 * </p>
 *
 * @author juiet
 * @since 2024年09月03日 10:35:39
 */
@Service
public class SysAccountServiceImpl extends ServiceImpl<SysAccountMapper, SysAccountEntity> implements ISysAccountService {

    @Value("${wsoft.rsa.privateKey}")
    private String privateKey;
    @Resource
    private IVerificationCodeService verificationCodeService;
    @Resource
    private RedisService redisService;
    @Resource
    private IMemberCommonService memberCommonService;
    @Resource
    private JwtTokenUtil jwtTokenUtil;
    @Resource
    private ITsConfigService tsConfigService;
    @Resource
    private ITsConfigInfoService tsConfigInfoService;
    @Resource
    private SysMemberMapper memberMapper;
    @Resource
    private ISysMemberService memberService;
    @Resource
    private ISysOrganizationService organizationService;
    @Resource
    private CommonConfigService commonConfigService;
    @Resource
    private ISysMemberOrgPostRelationService memberOrgPostRelationService;
    @Resource
    private CommonOrgService commonOrgService;

    private final String[] LOGIN_CONFIG = {"login_is_code", "system_name", "system _logo", "login_bg",
            "login_error_count" ,"copy_right" ,"tabs_max_num","login_logo","navigation_logo"};

    @Override
    public String login(LoginDTO dto) {
        List<ConfigRespVO> systemConfig = getSystemConfig(dto.getUsername());
        //获取是否启用验证码
        ConfigRespVO isCodeConfig = systemConfig.stream().filter(item -> item.getCode().equals(ConfigConstant.LOGIN_IS_CODE)).findFirst().get();
        if (LoginCodeEnum.ENABLE.getValue().equals(isCodeConfig.getValue())) {
            //1、验证验证码
            verificationCodeService.checkImageCode(dto.getImageKey(), dto.getImageCode());
        }
        //2、失败登录次数验证
        String keyVerification = RedisKeyConstant.LOGIN_USER_COUNT + dto.getUsername();
        int count = redisService.get(keyVerification) == null ? 0 : Integer.parseInt(redisService.get(keyVerification).toString());
        //获取密码错误次数
        ConfigRespVO errorCount = systemConfig.stream().filter(item -> item.getCode().equals(ConfigConstant.LOGIN_ERROR_COUNT)).findFirst().get();
        int loginErrorCount = Integer.parseInt(errorCount.getValue());
        if (count > loginErrorCount) {
            //连续登录超过5次,请5分钟后再试！
            Asserts.fail("[login.error.times]");
        }
        redisService.set(keyVerification, count + 1, 300);

        //3、密码验证
        UserDetails userDetails = memberCommonService.loadUserByUsername(dto.getUsername());
        if (userDetails == null) {
            /*用户名不存在*/
            Asserts.fail("[login.error.noUser]");
        }
        String dbPw = userDetails.getPassword();
        String rsaPassword = RsaUtil.decryptDataOnJava(dto.getPassword(), privateKey);
        if (!StrUtil.equals(dbPw, rsaPassword)) {
            /*密码错误*/
            Asserts.fail("[login.error.password]");
        }

        //4、生成token
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authentication);
        String token = jwtTokenUtil.generateToken(userDetails);

        //5、重置登录次数
        redisService.set(keyVerification, 0, 300);
        return token;
    }

    @Override
    public List<ConfigRespVO> getSystemConfig(String username) {
        // 总配置
        List<TsConfigEntity> list = tsConfigService.listConfigCache();
        if (CollUtil.isEmpty(list)) {
            Asserts.fail("[config.error.empty]");
        }
        List<String> codeList = Arrays.asList(LOGIN_CONFIG);

        // 查询用户信息
        LoginUserDetail loginUserDetail = (LoginUserDetail) memberCommonService.loadUserByUsername(username);

        //如果用户为空，拉取配置表
        if (StrUtil.isBlank(username) || loginUserDetail == null) {
            return list.stream().filter(e -> codeList.contains(e.getCode())).map(s -> Convert.convert(ConfigRespVO.class, s)).collect(Collectors.toList());
        }
        LoginUserVO loginUserVO = loginUserDetail.getLoginUserVO();
        Long memberId = loginUserVO.getId();
        Long loginOrgId = loginUserVO.getLoginOrgId();


        List<ConfigRespVO> respVOList = new ArrayList<>();
        codeList.forEach(e -> {
            ConfigRespVO configByCode = tsConfigInfoService.getConfigByCode(memberId, loginOrgId, e);
            respVOList.add(configByCode);
        });
        return respVOList;
    }


    @Override
    public LoginInfoRespVO getLoginInfo() {
        LoginUserDetail loginUserDetail = LoginUserUtil.getUserInfo();
        if (loginUserDetail == null) {
            Asserts.fail("[member.error.info.empty]");
        }

        LoginInfoRespVO respVO = new LoginInfoRespVO();
        respVO.setLoginUser(loginUserDetail.getLoginUserVO());
        respVO.setConfigList(getSystemConfig(loginUserDetail.getUsername()));
        List<Long> roleIds = loginUserDetail.getLoginUserVO().getRoles().stream().map(RoleVO::getId).collect(Collectors.toList());
        respVO.setMenuList(memberService.treeMenuListByRoleIds(roleIds));
        return respVO;
    }

    @Resource
    private IMemberCacheService memberCacheService;

    @Override
    public MultipleOrgRespVO getMultipleOrg() {
        LoginUserDetail loginUserDetail = LoginUserUtil.getUserInfo();
        if (loginUserDetail == null) {
            Asserts.fail("[member.error.info.empty]");
        }

        MultipleOrgRespVO respVO = new MultipleOrgRespVO();

        LoginUserVO loginUserVO = loginUserDetail.getLoginUserVO();
        List<SysOrganizationEntity> organizations = organizationService.selectOrgBy(loginUserVO.getId());
        if (CollUtil.isEmpty(organizations)) {
            Asserts.fail("[org.relation.error.empty]");
        }
        doWithSameOrgName(organizations);
        respVO.setOrgList(organizations);

        return respVO;
    }

    private void doWithSameOrgName(List<SysOrganizationEntity> orgs) {
        List<SysOrganizationEntity> list = organizationService.listAllOrgWithCache();
        Map<Long, SysOrganizationEntity> orgMap = list.stream().collect(Collectors.toMap(SysOrganizationEntity::getOrgId, Function.identity()));

        if (CollUtil.isEmpty(orgs)) {
            return;
        }
        Map<String, List<SysOrganizationEntity>> nameMap = orgs.stream().collect(Collectors.groupingBy(SysOrganizationEntity::getName));
        for (Map.Entry<String, List<SysOrganizationEntity>> each : nameMap.entrySet()) {
            List<SysOrganizationEntity> eachValue = each.getValue();
            if (eachValue != null && eachValue.size() > 1) {
                // 同名重复处理（parent+name）
                for (SysOrganizationEntity e : eachValue) {
                    // 获取上级组织名称
                    SysOrganizationEntity thisOrg = orgMap.get(e.getId());
                    Long paId = thisOrg.getPaId();
                    if (paId != null) {
                        SysOrganizationEntity parentOrg = orgMap.get(paId);
                        e.setName(parentOrg.getName() + "-" + e.getName());
                    }
                }
            }
        }
    }


    @Override
    public void changeLoginOrg(Long orgId) {
        LoginUserDetail loginUserDetail = LoginUserUtil.getUserInfo();
        if (loginUserDetail == null) {
            Asserts.fail("[member.error.info.empty]");
        }

        LoginUserVO loginUserVO = loginUserDetail.getLoginUserVO();
        List<SysOrganizationEntity> organizations = organizationService.selectOrgBy(loginUserVO.getId());
        if (CollUtil.isEmpty(organizations)) {
            Asserts.fail("[org.relation.error.empty]");
        }

        Optional<SysOrganizationEntity> find = organizations.stream().filter(e -> e.getId() != null && e.getId().equals(orgId)).findFirst();
        if (!find.isPresent()) {
            Asserts.fail("[org.relation.error.change.failed.auth]");
        }

        // 更新登录组织
        SysOrganizationEntity organization = find.get();
        int update = memberMapper.update(Wrappers
                .lambdaUpdate(SysMemberEntity.class)
                .set(SysMemberEntity::getLoginOrgId, organization.getOrgId())
                .set(SysMemberEntity::getLoginOrgCode, organization.getCode())
                .set(SysMemberEntity::getLoginOrgName, organization.getName())
                .eq(BaseEntity::getId, loginUserVO.getId())
        );
        if (update > 0) {
            // 缓存数据
            loginUserVO.setLoginOrgId(organization.getOrgId());
            loginUserVO.setLoginOrgCode(organization.getCode());
            loginUserVO.setLoginOrgName(organization.getName());
            memberCacheService.cacheMember(loginUserDetail);
            memberCommonService.loadUserByUsername(loginUserVO.getLoginName());
        }
    }


    @Override
    public void updatePwd(PwdQuery query) {
        Long memberId = LoginUserUtil.getUserInfo().getLoginUserVO().getId();
        QueryWrapper<SysAccountEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(SysAccountEntity::getMemberId, memberId);
        SysAccountEntity entity = getOne(queryWrapper);
        //密码原文
        String rsaPassword = RsaUtil.decryptDataOnJava(query.getOldPassword(), privateKey);
        //sm3加密
        String encrypt = Sm3EncryptionUtil.encrypt(rsaPassword);
        if (!StrUtil.equals(entity.getLoginPw(), encrypt)) {
            /*密码错误*/
            Asserts.fail("[login.error.password]");
        }
        String rsaNewPassword = RsaUtil.decryptDataOnJava(query.getNewPassword(), privateKey);
        String rsaConfirmPassword = RsaUtil.decryptDataOnJava(query.getConfirmPassword(), privateKey);
        if (!StrUtil.equals(rsaNewPassword, rsaConfirmPassword)) {
            /*确认密码错误*/
            Asserts.fail("[login.inconsistent.password]");
        }

        // 验证密码强度
        PasswordLevelEnum passwordLevelEnum = PasswordValidator.checkPasswordStrength(rsaPassword);
        int level = Integer.parseInt(passwordLevelEnum.getCode());
        //获取密码等级
        LoginUserVO loginUserVO = LoginUserUtil.getUserInfo().getLoginUserVO();
        ConfigQuery config = new ConfigQuery();
        config.setOrgId(loginUserVO.getLoginOrgId());
        config.setMemberId(loginUserVO.getId());
        config.setCode(ConfigConstant.LOGIN_LEVEL);
        Map<String, String> value = commonConfigService.getConfigValue(config);
        int loginLevel = Integer.parseInt(value.get("value"));
        if (level < loginLevel) {
            Asserts.fail("[login.error.password]");
        }
        boolean b = update(Wrappers
                .lambdaUpdate(SysAccountEntity.class)
                .set(SysAccountEntity::getLoginPw, Sm3EncryptionUtil.encrypt(rsaNewPassword))
                .eq(SysAccountEntity::getMemberId, memberId)
        );
        if (b) {
            //更新缓存
            redisService.del(USER_INFO_CACHE + entity.getLoginName());
            memberCommonService.loadUserByUsername(entity.getLoginName());
        }
    }

    @Override
    public void updateStyle(StyleQuery query) {
        LoginUserVO loginUserVO = LoginUserUtil.getUserInfo().getLoginUserVO();
        update(Wrappers
                .lambdaUpdate(SysAccountEntity.class)
                .set(SysAccountEntity::getConfigLayout, query.getConfigLayout())
                .set(SysAccountEntity::getConfigLocation, query.getConfigLocation())
                .set(SysAccountEntity::getConfigSkin, query.getConfigSkin())
                .set(SysAccountEntity::getConfigCustomSkin, query.getConfigCustomSkin())
                .eq(SysAccountEntity::getMemberId, loginUserVO.getId())
        );
        //更新缓存
        redisService.del(USER_INFO_CACHE + loginUserVO.getLoginName());
        memberCommonService.loadUserByUsername(loginUserVO.getLoginName());
    }

    @Override
    public void updateOnline(OnlineQuery query) {
        update(Wrappers
                .lambdaUpdate(SysAccountEntity.class)
                .set(SysAccountEntity::getIsOnline, query.getIsOnline())
                .set(SysAccountEntity::getOnlineTime, LocalDateTime.now())
                .eq(SysAccountEntity::getMemberId, query.getMemberId())
        );
    }


    @Override
    @GetRedisCache(type = RedisKeyConstant.ACCOUNT_ALL_CACHE,
            value = RedisKeyConstant.REDIS_NULL)
    public List<SysAccountEntity> listAll() {
        return list();
    }

    @Override
    public Long getOrgIdByType(String type) {
        LoginUserVO loginUserVO = LoginUserUtil.getUserInfo().getLoginUserVO();
        Long loginOrgId = loginUserVO.getLoginOrgId();
        OrganizationVO orgInfo = commonOrgService.getOrgInfo(loginOrgId);
        if (orgInfo.getType().equals(type)){
            return orgInfo.getOrgId();
        }
            List<OrganizationVO> parentOrg = commonOrgService.getParentOrg(loginOrgId);
            List<OrganizationVO> collect = parentOrg.stream().filter(e -> {
                return e.getType().equals(type);
            }).collect(Collectors.toList());
            if (collect.size()>0){
                return collect.get(0).getOrgId();
            }
        return null;
    }


}
